#pragma once

#include <stdexcept>
template <typename T> 
class UniquePtr
{
private:
    T * _p;
public:
    UniquePtr();
    UniquePtr(UniquePtr && other);
    explicit UniquePtr(T * _p);

    UniquePtr& operator=(UniquePtr && other) {
        if( this != other ) {
            _p = other._p;
            other._p = nullptr;
        }
        return *this;
    }

    ~UniquePtr();
    UniquePtr(const UniquePtr& other) = delete;
    UniquePtr& operator=(const UniquePtr& other) = delete;

    T * get();
    void reset();
    void reset(T* newone);
    T* release();

    T& operator*() {
        if( _p ==nullptr ) {
            throw std::logic_error("resolve null ptr");
        }
        return *_p;
    }
    T* operator->() {
        if( _p ==nullptr ) {
            throw std::logic_error("resolve null ptr");
        }
        return _p;
    }

    explicit operator bool() {
        return _p != nullptr;
    }
};

template <typename T>
UniquePtr<T>::UniquePtr() : _p(nullptr) {}

template <typename T>
UniquePtr<T>::UniquePtr(T * _p) : _p(_p) {}

template <typename T>
UniquePtr<T>::~UniquePtr()
{
    if(_p)
        delete _p;
}

template <typename T>
UniquePtr<T>::UniquePtr(UniquePtr && other) : _p(other._p) {
    other._p = nullptr;
}

template <typename T, typename ...Args>
UniquePtr<T> make_unique(Args ...args) {
    T * newptr = new T(args...);
    return UniquePtr<T>(newptr);
}

template <typename T>
T* UniquePtr<T>::get() {
    return _p;
}

template <typename T>
void UniquePtr<T>::reset() {
    if(_p)
        delete _p;
    _p = nullptr;
}

template <typename T>
void UniquePtr<T>::reset(T *newone) {
    if(_p)
        delete _p;
    _p = newone;
}

template <typename T>
T* UniquePtr<T>::release() {
    T* temp = _p;
    _p = nullptr ;
    return temp;
}